package com.sunxboy.platform.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;

public class FtpClientServiceImpl {
    private static final Logger LOG = Logger.getLogger(FtpClientServiceImpl.class);

    private static final String FILEEXT_TEMP = ".tmp";
    private static final String PATH_SEPERATOR = "/";

    private final String host;
    private final String userName;
    private final String passWord;
    private final String remotePath;

    public FtpClientServiceImpl(final Map<String, String> ftpConfigMap) {

        this.host = ftpConfigMap.get("HOST");
        this.userName = ftpConfigMap.get("USERNAME");
        this.passWord = ftpConfigMap.get("PASSWORD");
        this.remotePath = ftpConfigMap.get("REMOTEPATH");
    }

    private boolean getFile(String fileNameWithLocalPath, String fileName, FTPClient ftpClient) throws IOException {

        boolean retVal = true;
        // file to write
        FileOutputStream fos = new FileOutputStream(new File(fileNameWithLocalPath));

        try {
            // get the file from remote path
            if (!ftpClient.retrieveFile(fileName, fos)) {

                retVal = false;
            }
        }

        finally {
            // flush and close outputstream
            fos.flush();
            fos.close();
        }
        return retVal;
    }

    /*
     * Used to validating the Credentials for FTP connect.
     */
    private boolean validateCredentials(final FTPClient ftpClient, final String userName, final String passWord, final String hostRemotePath,
            final String appendToRemotePath) throws Exception {

        // Get all the required properties from Source Factory Bean
        String remotePath = hostRemotePath + PATH_SEPERATOR + appendToRemotePath;
        int reply;
        reply = ftpClient.getReplyCode();

        // Validate the reply code
        if (!FTPReply.isPositiveCompletion(reply)) {
            LOG.error("Unable to connect to ftp server");
            return false;
        }

        // Validate user/password
        if (!ftpClient.login(userName, passWord)) {
            LOG.error("Unable to login to ftp server");
            return false;
        }

        // Validate if able to change to required working directory
        if (!ftpClient.changeWorkingDirectory(remotePath)) {
            LOG.error("Unable to change working directory");
            return false;
        }

        // Set File Type
        if (!ftpClient.setFileType(FTP.ASCII_FILE_TYPE)) {
            LOG.error("Unable to set file mode ");
            return false;
        }

        return true;

    }

    /*
     * To send the file to TODO needs to be tested this modified version sendFile method
     */
    public boolean sendFile(final File file, final String destinationFileName) {
        boolean allDone = false;
        FTPClient ftpClient = new FTPClient();
        String hostName = host;
        try {
            // Connect to FTP Server
            ftpClient.connect(hostName);

            // Validate the credentials
            if (validateCredentials(ftpClient, userName, passWord, remotePath, "")) {

                // Using Temp File, will delete this temp file later
                // Not sure exactly Why Temp file is used, needs to re-look at this
                String tempFileName = destinationFileName + FILEEXT_TEMP;

                FileInputStream fis = new FileInputStream(file);
                try {
                    // Store the file
                    ftpClient.storeFile(tempFileName, fis);

                }
                finally {
                    // make sure fis is closed
                    fis.close();
                }

                // Rename the temp file to require name
                String finalFileName = destinationFileName;
                if (!ftpClient.rename(tempFileName, finalFileName)) {
                    if (!ftpClient.deleteFile(tempFileName)) {
                        LOG.warn("Unable to delete temp file");
                    }
                    throw new RuntimeException("Unable to rename file");
                }

                // Logout
                ftpClient.logout();
                allDone = true;

            }
            else {

                allDone = false;
                new RuntimeException("Error Validating Credentials to FTP Server");

            }

        }
        catch (Exception e) {
            LOG.fatal("Unable to FTP file", e);
        }
        finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                }
                catch (IOException e) {
                    LOG.warn("Error while disconnecting from FTP", e);
                }
            }
        }

        return allDone;
    }

    // This will download from FTPServer all the files from path mentioned in ftpConfig.xml.
    // Need to give Local Path as a parameter to store files.
    public boolean downloadAllFiles(final String completeLocalPath) {
        return downloadFiles("", completeLocalPath, new ArrayList<String>());
    }

    // This will download from FTPServer all the files from path mentioned in ftpConfig.xml.
    // Need to give Path to Append to Remote Path of ftpConfig.xml as a parameter
    // Need to give Local Path as a parameter to store files.
    public boolean downloadAllFiles(final String appendToRemotePath, final String completeLocalPath) {
        return downloadFiles(appendToRemotePath, completeLocalPath, new ArrayList<String>());
    }

    // This will download from FTPServer all the files.
    // It gives privilege to append any path to remote path mentioned in ftpConfig.xml.
    // Need to give params
    // 1) Path to Append to Remote Path of ftpConfig.xml as a parameter
    // 2) Local Path to store files.
    // 3) List of Files to download(Empty if all the files)
    public boolean downloadFiles(final String appendToRemotePath, final String completeLocalPath, final List<String> fileNames) {
        boolean allDone = false;
        FTPClient ftpClient = new FTPClient();
        String hostName = host;
        try {
            // Connect to FTP Server
            ftpClient.connect(hostName);

            // Validate the credentials
            if (validateCredentials(ftpClient, userName, passWord, remotePath, appendToRemotePath)) {
                int count = 0;

                // If the list is Empty retrieve all the files else only files in the list
                if (fileNames.isEmpty()) {

                    // Get the list of all the files present in the remote path
                    FTPFile[] files = ftpClient.listFiles(remotePath + PATH_SEPERATOR + appendToRemotePath);

                    for (FTPFile ftpFile : files) {

                        String fileName = ftpFile.getName();
                        LOG.info("File Name - " + fileName);

                        String fileNameWithLocalPath = completeLocalPath + fileName;

                        if (!this.getFile(fileNameWithLocalPath, fileName, ftpClient)) {

                            allDone = false;
                            LOG.error("File " + fileName + " could not be retrieved ... ");

                        }

                        count++;
                    }

                    LOG.info("Number of Files Ftped = " + count);

                }
                else {

                    for (String fileName : fileNames) {

                        LOG.info("File Name - " + fileName);

                        String fileNameWithLocalPath = completeLocalPath + fileName;

                        if (!this.getFile(fileNameWithLocalPath, fileName, ftpClient)) {

                            allDone = false;
                            LOG.error("File " + fileName + " could not be retrieved ... ");

                        }

                        count++;
                    }
                    LOG.info("Number of Files Ftped = " + count);

                }

                // Logout
                ftpClient.logout();
                allDone = true;

            }
            else {

                allDone = false;
                new RuntimeException("Error Validating Credentials to FTP Server");

            }

        }
        catch (Exception e) {
            LOG.fatal("Unable to FTP file", e);
        }
        finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                }
                catch (IOException e) {
                    LOG.warn("Error while disconnecting from FTP", e);
                }
            }
        }

        return allDone;
    }

}
